# Renderless Components в React. Как интегрировать неинтегрируемое?

## Вступление

###### intro
Привет DevPRO! Сегодня я хочу рассказать вам про Renderless Components в React.

###### about
Для начала - немного о себе: меня зовут Сухушин Александр. Я - Frontend-разработчик в компании **Userstory** (навести на ссылку).
Наша компания занимается проектированием и разработкой сайтов и комплексных информационных систем для автоматизации бизнеса, (убрать наведение с сылки)
а также решениями коммерческих и маркетинговых задач.

###### stack
Мы делаем фронтенд на React.

###### stack
В своём последнем проекте нам нужно было работать с картами, а также обновлнять состояние на клиенте по WebSocket.
Мы использовали _Leaflet_, на данный момент - это самая известная OpenSource библиотека для работы с картами.
Для работы с WebSocket мы использовали билиотеку Centrifuge.
Основная проблема этих двух библиотек то, что у них нет реализации для _React_.

###### react−leaflet
На самом деле есть _React-Leaflet_, но нам нужно было использовать сторонние плагины, которые он не поддерживал и не давал нужной гибкости.
Что же делать?

###### leaflet
Для начала давайте посмотрим, как работает сам _LeafLet_, заглянем в официальную документацию:
Чтобы создать карту, нужно создать `div`, который будет выступать в качестве контейнера для карты.
Далее в JavaScript нужно вызвать метод `L.map()`, куда передать идентификатор контейнера или ссылку на его узел _DOM_.
Также, нужно спозиционировать карту, в данном случае задаются координаты центра и масштаб.
И ещё нужно насторить Тайл-сервер - это сервер, который будет возвращать картинки для карты.

###### map
Мы можем реализовать это в React следующим образом: В методе render, мы срздаём `div` и сохраняем ссылку на него с помощью ref.
В `componentDidMount` мы вызываем метод `L.map()`, куда передаём ссылку на созданый контейнер, и сохраняем экземпляр в `state`.
В `componentWillUnmount` мы удаляем созданный экземпляр карты.
Так же через контекст мы передаём дочерним элементам созданый экземпляр `leaflet`.

###### tile-layer
Тайл-сервер: `render` - `null`. В `componentDidMount`, мы добавляем слой тайл-сервера, в `componentWillUnmount` - его удаляем.
В `componentDidUpdate` - удаляем старый слой и добавляем новый. добавление и удаление слоёв - это уже вызовы методов _Leaflet_,
которые можно посмотреть в документации.

###### map-geo-json
Подобным же образом выводятся объекты на карте. На `componentDidMount`, мы добавляем слой, на `componentWillUnmount` - удаляем его.
На `componentDidUpdate` - удаляем старый слой и добавляем новый.

###### geo-json
Для описания объектов на карте используется формат _GeoJSON_. По сути, это обычный _JSON_, с определённой структурой.
Для примера, я описал несколько объектов:

###### map-view
С позиционированием немного посложнее: Самый простой способ - это задать координаты центра и масштаб,
но по проекту требовалось позиционировать карту, чтобы у неё был максимальный масштаб, но при этом в экран попадали все объекты.
В `componentDidMount` я вызываю метод `fly()`, который получает `view` - это объект или массив объектов в формате _GeoJSON_.
Из `view` мы получаем `bounds`.

###### bounds
`bounds` - это минимальная прямоуголяная область, которая включает все объекты.

###### map-view-2
Если `bounds` - корректный - мы позиционируем карту на нём, иначе показываем весь мир.
В `componentDidUpdate` я проверяю, изменился ли `view`,  изменился ли `bounds` если да, вызываю `fly()`.
И также я добавил обработчик измнения позиционирования карты, который вызывает `onViewChange`, если он был передан в `props`.

###### map-view-joke
Я вижу, вы уже немного устали от кода. На самом деле есть такая тема, когда долго смотришь на код (нажать картинку), код начинает смотреть на тебя.

###### map-usage
Давайте посмотрим пример, как это выглядит в использовании: Выводится компонент Map, а внутри него - `TileLayer`, `View`, и объекты на карте.

###### [map-example](https://suhushinas.github.io/2019-04-27_renderless-components-example/#/example-map)
И, наконец, посмотрим на результат, можете перейти по этому коду:
(подождать пару минут) 
Здесь мы можем менять позиционирование карты, при этом фиксируется текущее положение.
Можем менять тайл сервер, например есть такой, и даже такой...
Также можем отображать на карте разные объекты: например, маркер, где проходит **DevPRO**, маркер, где находится наш новый офис, и путь, между ними.
При этом, как вы видите, карта автоматически позиционируется по выводимым объектам.
(Закрыть Ctrl+W)

###### socket-centrifuge
Как вы заметили, во многих компонентах, я ничего не выводил, а пользовался только методами жизненного цикла.
Такой подход я назвал Renderless Components. И он позволяет интегрирровать практически что угодно:
Например так я интегрировал WebSocket:
`componentDidMount` - я подключаюсь к серверу центрифуги,
`componentWillUnmount` - отключаюсь, и передаю экзепляр центрифуги через контекст дочерним элементам.

###### socket-subscribe
Аналогично - компонент для подписки `componentDidMount` - подписывемся, `componentWillUnmount` - отписываемся.
Во время подписки - регистрируем обработчик сообщений.

###### socket-usage
В использовании это выглядит вот так: Мы оборачиваем всё в компонент `Centrifuge`, куда передаём данные для подключения.
Внутри выводим компонент `Subscribe`, куда передаём название канала и обработчик сообщения.
По WebSocket мы будем получать координаты точки и выводить её на карте.

###### [socket-example](https://suhushinas.github.io/2019-04-27_renderless-components-example/#/example-socket)
Я подготовил пример, как по сокетам мы будем получать координаты точки
(подождать пару минут):

###### questions
По этомим кодам ссылки на презентацию и исходный код примеров.
На этом у меня всё, пожалуйста ваши вопросы.
